#include "SoftSerial.h"

void (_SoftSerial::*_t1ISR)();
void (_SoftSerial::*_i0ISR)();

void _SoftSerial::begin(int baud){
	_ptrRead=0;
	_ptrWrite=0;
	pinMode(PIN2,INPUT);

	_t1ISR=&_SoftSerial::_Timer1InterruptRoutine;
	_i0ISR=&_SoftSerial::_Int0InterruptRoutine;

	_baud=2000000/baud;    //Calculate baud (based on 16MHz processor)

	SREG|=B10000000;       //Global interrupt enable

	// Setup timer1 to interrupt on overflow, internal clock, 1:8 prescaler
	TCCR1A=0;
	TCCR1B=B00000010;
	TIMSK1=B00000000;

	//External Interrupt enable on pin2 falling edge
	EICRA=B00000010;
	EIMSK=B00000001;
}

void _SoftSerial::_Timer1InterruptRoutine(){
	// reset timer to trigger in next bit
	TCNT1=0xFFFF-_baud;
	//if less than eight bytes have been written
	if(_i<8){
		_b|=digitalRead(PIN2)<<_i;  //read the pin and add it to the temp byte
		_i++;
	}
	else{  //when all bytes have been received
		buf[_ptrWrite++]=_b;       //Add the temp byte to the buffer
		_ptrWrite%=BUFFER_SIZE;
		if(_ptrWrite==_ptrRead)
			_ptrRead++;
		TIMSK1=B00000000;          //Switch off the timer
		EIMSK=B00000001;           //Re-enable the falling edge interrupt
		EIFR=B00000011;
	}
}


ISR(TIMER1_OVF_vect){
	(SoftSerial.*_t1ISR)();
}

//Fired when falling edge detected on pin 2
void _SoftSerial::_Int0InterruptRoutine(){
	//reset temporary storage variables
	_b=0;
	_i=0;

	//Disable external interrupt for now
	EIMSK=B00000000;

	//Enable timer and set it to overflow in 1.5*baud ticks, in middle of bit0
	TIFR1=B00000111;
	TIMSK1=B00000001;
	TCNT1=0xFFFF-_baud;
}

ISR(INT0_vect){
	(SoftSerial.*_i0ISR)();
}

// Read a byte from the buffer and increment the pointer
int _SoftSerial::read(void){
	byte b;
	if(available()){
		b=buf[_ptrRead++];
		_ptrRead%=BUFFER_SIZE;
		return b;
	}
	else
		return -1;
}

int _SoftSerial::peek(void){
	byte b;
	if(available()){
		b=buf[_ptrRead];
		return b;
	}
	else
		return -1;
}

// Check if there are bytes available to read
int _SoftSerial::available(void){
	if(_ptrWrite<_ptrRead)
		return _ptrWrite+BUFFER_SIZE-_ptrRead;
	else
		return _ptrWrite-_ptrRead;
}

_SoftSerial SoftSerial;
